package org.aeon.gamechatnest.configuration.security;


import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aeon.gamechatnest.configuration.security.handler.LoginAccessDefineHandler;
import org.aeon.gamechatnest.configuration.security.handler.LoginAuthenticationHandler;
import org.aeon.gamechatnest.dto.UserLoginDto;
import org.aeon.gamechatnest.util.JsonUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Collections;

/**
 * @author: MissingAeon
 * @date: 2024/5/8 18:23
 * @description: SpringSecurity配置类
 */
@Slf4j
@Profile("sec")
@EnableWebSecurity  // 启用Spring Security
@EnableMethodSecurity  // 启用方法安全设置
@Configuration
public class WebSecurityConfiguration {
    @Resource
    private LoginAuthenticationHandler loginAuthenticationHandler;
    @Resource
    private LoginAccessDefineHandler loginAccessDefineHandler;
    @Resource
    private CustomerUserDetailsService userDetailsService;
    @Resource
    private JwtFilter jwtFilter;

    // public WebSecurityConfiguration() {
    //     log.debug("WebSecurityConfiguration装配");
    // }

    /**
     * 密码处理
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    /**
     * 新版的实现方法不再和旧版一样在配置类里面重写方法，而是构建了一个过滤链对象并通过@Bean注解注入到IOC容器中
     * 新版整体代码 （注意：新版AuthenticationManager认证管理器默认全局）
     *
     * @param http http安全配置
     * @return SecurityFilterChain
     * @throws Exception 异常
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http    // 使用自己自定义的过滤器 去过滤接口请求
                .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
                // .formLogin((formLogin) ->
                //         // 这里更改SpringSecurity的认证接口地址，这样就默认处理这个接口的登录请求了
                //         formLogin.loginProcessingUrl("/user/login")
                //                 //　自定义的登录验证成功或失败后的去向
                //                 .successHandler(loginSuccessHandler)
                //                 .failureHandler(loginFiledHandler)
                // )
                // 禁用了 CSRF 保护。
                .csrf(AbstractHttpConfigurer::disable)
                // 配置了会话管理策略为 STATELESS（无状态）。
                // 在无状态的会话管理策略下，应用程序不会创建或使用 HTTP 会话，每个请求都是独立的，服务器不会在请求之间保留任何状态信息。
                .sessionManagement((sessionManagement) -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests((authorizeRequests) ->
                        // 这里过滤一些 不需要token的接口地址
                        authorizeRequests
                                .requestMatchers("/user/login", "/user/register", "/captchaImage").permitAll()
                                .requestMatchers("/swagger-ui/*").anonymous()
                                .requestMatchers("/swagger-resources/**").anonymous()
                                .requestMatchers("/webjars/**").anonymous()
                                .requestMatchers("/*/api-docs", "/api-docs/*", "/api-docs", "/swagger").anonymous()
                                .requestMatchers("/druid/**").anonymous()
                                // socket 请求不需要拦截
                                .requestMatchers("/chat/message/**").anonymous()
                                .anyRequest().authenticated())
                .exceptionHandling((exceptionHandling) -> exceptionHandling
                        .authenticationEntryPoint(loginAuthenticationHandler) // 匿名处理
                        .accessDeniedHandler(loginAccessDefineHandler)  // 无权限处理
                )
                .cors((cors) -> cors.configurationSource(configurationSource()))
                .headers((headers) -> headers.frameOptions((HeadersConfigurer.FrameOptionsConfig::disable)))
                .headers((headers) -> headers.frameOptions((HeadersConfigurer.FrameOptionsConfig::sameOrigin)));
        // 构建过滤链并返回
        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder) {
        return (authentication) -> {
            UserLoginDto userLogin = (UserLoginDto) authentication.getPrincipal();
            String password = authentication.getCredentials().toString();
            try {
                // 从数据库或其他数据源获取用户信息
                UserDetails userDetails = userDetailsService.loadUserByUsername(JsonUtil.obj2String(userLogin));
                // 验证密码是否正确
                if (passwordEncoder.matches(password, userDetails.getPassword())) {
                    return new UsernamePasswordAuthenticationToken(userDetails, null, null);
                } else {
                    throw new BadCredentialsException("密码错误");
                }
            } catch (UsernameNotFoundException e) {
                throw new BadCredentialsException("用户名不存在");
            }
        };
    }

    /**
     * 跨域配置
     */
    CorsConfigurationSource configurationSource() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedHeaders(Collections.singletonList("*"));
        corsConfiguration.setAllowedMethods(Collections.singletonList("*"));
        corsConfiguration.setAllowedOrigins(Collections.singletonList("*"));
        corsConfiguration.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }


}
